// file: 'sudhir_main.cpp'
// author: Sudhir
// main.cpp used for testing the API. Please do not use/edit this file.

#include <iostream>
#include "PKB\AST\AST_Builder.h"
#include "PKB\AST\AST_Query.h"
#include "PKB\AST\AST_XLab2.h"

#include "PKB\DE\DesignExtractor.h"

#include "SPA Front End\Parser2.h"

#include <boost\algorithm\string.hpp>

#include "Query_Preprocessor.h"
#include "PKB\TABLES\AuxilaryTable.h"

#include <time.h>
#include <iterator>
#include <functional>

using namespace std;

void sudhir_waitOnExit() {
	//cout << "press enter to continue..." ; cin.get();
	cout << endl << "\npress enter to continue...";
	cin.get();
};
void parseExpr( expr *e ) {
	expr *currExpr = e;

	if( currExpr->toString() == "+" 
		|| currExpr->toString() == "-"
		|| currExpr->toString() == "*" ) {
			cout << currExpr->toString() << " ";
			parseExpr( ((composite_expr*)currExpr)->getLHS());
			parseExpr( ((composite_expr*)currExpr)->getRHS());
	}
	else {
		cout << currExpr->toString() << " ";
		//s->pop();
	}
};
void testLinks_reverse ( AST_Query aq) {
	aq.gotoStmtAt ( aq.numStmts() -1 );
	cout << endl << "Query API testing..." << endl;
	cout << "========================" << endl;
	cout << "link types::  F N W T E " << endl;
	cout << "========================" << endl;
	do {
		cout << ": curr: " << aq.getCurrStmtIndex() << " :: ";
		for( int i=0; i<stmtNode::numLinks; i++) {
			cout << " ";
			if( aq.getLinksId()[i] == stmtNode::INVALID_INDEX) 
				cout << "-";
			else cout << aq.getLinksId()[i];
		}
		if( aq.getCurrStmtType() == STATEMENT_Assign ) {
			cout << " :ASSIGN :: " << aq.getLHS()->toString() << " = ";
			parseExpr(aq.getRHS());
		}
		cout << endl;
	}
	while( aq.gotoPrevLine() == STATUS_Success);
};
void testLinks_forward ( AST_Query aq) {
	// start traversing from statement munber 1
	if( aq.gotoStmtAt ( 1 ) == STATUS_Success) {
		INDEX *linksId = new INDEX[stmtNode::numLinks];
		cout << endl << "Query API testing..." << endl;
		cout << "==============\t\t============" << endl;
		cout << "link types::  \t\t F P N W T E " << endl;
		cout << "==============\t\t============" << endl;
		linksId = aq.getLinksId();
		// traverse until no more lines in program
		do{
			cout << ": curr: " << aq.getCurrStmtIndex();
			cout << "\tp[" << aq.getCurrProcID() <<"] :: ";
			linksId = aq.getLinksId();
			for( int i=0; i<stmtNode::numLinks; i++) {
				cout << " ";
				if( linksId[i] == stmtNode::INVALID_INDEX) 
					cout << "-";
				else cout << linksId[i];
			}
			if( aq.getCurrStmtType() == STATEMENT_Assign ) {
				cout << "\t:ASSIGN :: " << aq.getLHS()->toString() << " = ";
				// <--debugg
				cout << aq.getSyntacticExpr();
				cout << endl;
				// -->
			} else if( aq.getCurrStmtType() == STATEMENT_Call ) {
				cout << "\t:CALL :: " << "proc: ( " << aq.getCallProcID() << " )\n";
			}else if( aq.getCurrStmtType() == STATEMENT_If ) {
				cout << "\t:IF :: " << "Var: ( " << aq.getConditionVar() << " )\n";
			}else if( aq.getCurrStmtType() == STATEMENT_While ) {
				cout << "\t:WHILE :: " << "Var: ( " << aq.getConditionVar() << " )\n";
			}
			cout << endl;
			if (linksId[stmtNode::FOLLOWS] == stmtNode::INVALID_INDEX 
				&& linksId[stmtNode::NEXT_STMT] == stmtNode::INVALID_INDEX 
				&& linksId[stmtNode::NEXT_TO_ELSE] == stmtNode::INVALID_INDEX 
				&& linksId[stmtNode::NEXT_TO_THEN] == stmtNode::INVALID_INDEX 
				&& linksId[stmtNode::NEXT_TO_WHILE] == stmtNode::INVALID_INDEX 
				&& linksId[stmtNode::PARENT] == stmtNode::INVALID_INDEX) {
					cout << "end of procedure: "<< aq.getCurrProcID() << endl;
			}
		}while( aq.gotoNextLine() == STATUS_Success );
	}
};

class myiterator : public iterator<input_iterator_tag, int>
{
	int* p;
public:
	myiterator(int* x) :p(x) {}
	myiterator(const myiterator& mit) : p(mit.p) {}
	myiterator& operator++() {++p;return *this;}
	myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
	bool operator==(const myiterator& rhs) {return p==rhs.p;}
	bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
	int& operator*() {return *p;}
};

typedef struct affectsNode {
	STATEMENT_INDEX stmtID;
	BOOLEAN isVisited;
} affectsNode;
struct findNode: public std::binary_function< affectsNode, STATEMENT_INDEX, BOOLEAN > {
	bool operator () ( const affectsNode &Node, const STATEMENT_INDEX &id ) const {
		return  (Node.stmtID == id );
	}
};

BOOLEAN containsAffects( list<affectsNode> &AffectsTreeVisitedNodes, list<affectsNode>::iterator &it,  STATEMENT_INDEX i ) {
	if( AffectsTreeVisitedNodes.size() >0 ) {
		it = AffectsTreeVisitedNodes.begin();
		it = find_if( it, AffectsTreeVisitedNodes.end(), std::bind2nd(findNode(), i ) );
		if( it != AffectsTreeVisitedNodes.end() && (*it).stmtID == i && (*it).isVisited==false ) 
			return true;
		else return false;
	} else return false;
};

BOOLEAN AffectsStar ( STATEMENT_INDEX s1, STATEMENT_INDEX s2, QueryProcessor &m, StatTable *st, ProcTable *p ) {
	if ( ( st->getType( s1 ) != STATEMENT_Assign ) ||  ( st->getType( s2 ) != STATEMENT_Assign ) ) return false;
	if ( p->getProcIn (s1) != p->getProcIn( s2 ) ) return false;
	list<affectsNode> AffectsTreeVisitedNodes;
	list<affectsNode>::iterator it;
	vector<STATEMENT_INDEX> v1;
	vector<STATEMENT_INDEX> v2;
	STATEMENT_INDEX currStmt = 0;

	affectsNode a;
	//
	Argument arg1, arg2;
	arg1.argumentType = INTARGUMENT;
	arg1.argument = s1;
	arg2.argumentType = ARGUMENT;
	arg2.argument = _STMT;
	//

	//m.getNextafterT(v1, v2, arg1, arg2);
	m.getAffecting(v1, v2, arg1, arg2);

	// push all affects of s1.
	for( int i=0; i<v2.size(); i++ ) {
		currStmt = v2.at(i);
		a.stmtID = currStmt; a.isVisited = false;
		AffectsTreeVisitedNodes.push_back( a );
	};
	while ( !AffectsTreeVisitedNodes.empty() ) { // O(n*n)
		arg1.argument = AffectsTreeVisitedNodes.back().stmtID;
		AffectsTreeVisitedNodes.pop_back();

		m.getAffecting(v1, v2, arg1, arg2); // O(
		for( int i=0; i<v2.size(); i++ ) {
			if( currStmt == s2 ) // found s2; therefore Affects*(s1, s2) is true
				return true;
			currStmt = v2.at(i);
			a.stmtID = currStmt; a.isVisited = false;
			if( !containsAffects( AffectsTreeVisitedNodes, it, currStmt ) )
				AffectsTreeVisitedNodes.push_back( a );
		};
		/*if( containsAffects( AffectsTreeVisitedNodes, it, s1 ) ) cout << "yay..";
		else cout << ":(";*/
	}
	return false;
};

BOOLEAN searchStmtList( STATEMENT_INDEX s1, vector<STATEMENT_INDEX> &searchVector, list<STATEMENT_INDEX>::iterator &it ) {
	for (unsigned int i=0; i<searchVector.size(); i++)
		if ( searchVector.at(i) == s1 )
			return true;
	return false;
};

BOOLEAN AffectsStarLong ( STATEMENT_INDEX s1, QueryProcessor &m, vector<STATEMENT_INDEX> &resVector ) {
	//list<affectsNode> AffectsTreeVisitedNodes;
	vector<STATEMENT_INDEX> UnVisitedNodes;
	vector<STATEMENT_INDEX> VisitedNodes;
	//list<affectsNode>::iterator it;
	list<STATEMENT_INDEX>::iterator iit;
	vector<STATEMENT_INDEX> v1;
	vector<STATEMENT_INDEX> v2;
	STATEMENT_INDEX currStmt = 0;

	affectsNode a;
	//
	Argument arg1, arg2;
	arg1.argumentType = INTARGUMENT;
	arg1.argument = s1;
	arg2.argumentType = ARGUMENT;
	arg2.argument = _STMT;
	//

	//m.getNextafterT(v1, v2, arg1, arg2);
	m.getAffecting(v1, v2, arg1, arg2);

	// push all affects of s1.
	for( int i=0; i<v2.size(); i++ ) {
		//currStmt = v2.at(i);
		//a.stmtID = currStmt; a.isVisited = false;
		//UnVisitedNodes.push_back( a.stmtID );
		UnVisitedNodes.push_back( v2.at(i) );
	};
	while ( !UnVisitedNodes.empty() ) { // O(n*n)
		//arg1.argument = AffectsTreeVisitedNodes.back().stmtID;
		arg1.argument = UnVisitedNodes.back();  // next node to be computed "k"
		//AffectsTreeVisitedNodes.back().isVisited = true;
		//AffectsTreeVisitedNodes.pop_back();

		//VisitedNodes.push_back( UnVisitedNodes.back() ); // 
		resVector.push_back( UnVisitedNodes.back() ); // 
		currStmt = UnVisitedNodes.back();
		UnVisitedNodes.pop_back();

		v2.clear();
		m.getAffecting(v1, v2, arg1, arg2); // get all statements that affect

		for( int i=0; i<v2.size(); i++ ) { // store all statements that affect "k"
			//if( currStmt == s2 ) // found s2; therefore Affects*(s1, s2) is true
			//	return true;
			//currStmt = v2.at(i);
			//a.stmtID = currStmt; a.isVisited = false;
			//if( !containsAffects( AffectsTreeVisitedNodes, it, currStmt ) )
			//AffectsTreeVisitedNodes.push_back( a );

			//if ( !searchStmtList(v2.at(i), VisitedNodes, iit ) && !searchStmtList(v2.at(i), UnVisitedNodes, iit ) )
			if ( !searchStmtList(v2.at(i), resVector, iit ) && !searchStmtList(v2.at(i), UnVisitedNodes, iit ) )
				UnVisitedNodes.push_back(v2.at(i));
		};
	}
	return false;
};

BOOLEAN AffectsStarTLong ( STATEMENT_INDEX s1, QueryProcessor &m, vector<STATEMENT_INDEX> &resVector ) {
	//list<affectsNode> AffectsTreeVisitedNodes;
	vector<STATEMENT_INDEX> UnVisitedNodes;
	vector<STATEMENT_INDEX> VisitedNodes;
	//list<affectsNode>::iterator it;
	list<STATEMENT_INDEX>::iterator iit;
	vector<STATEMENT_INDEX> v1;
	vector<STATEMENT_INDEX> v2;
	STATEMENT_INDEX currStmt = 0;

	affectsNode a;
	//
	Argument arg1, arg2;
	arg1.argumentType = ARGUMENT;
	arg1.argument = _STMT;
	arg2.argumentType = INTARGUMENT;
	arg2.argument = s1;
	//

	//m.getNextafterT(v1, v2, arg1, arg2);
	m.getAffectedby(v1, v2, arg1.argument, arg2.argument,arg2.argumentType);

	// push all affects of s1.
	for( int i=0; i<v1.size(); i++ ) {
		//currStmt = v2.at(i);
		//a.stmtID = currStmt; a.isVisited = false;
		//UnVisitedNodes.push_back( a.stmtID );
		UnVisitedNodes.push_back( v1.at(i) );
	};
	while ( !UnVisitedNodes.empty() ) { // O(n*n)
		//arg1.argument = AffectsTreeVisitedNodes.back().stmtID;
		arg2.argument = UnVisitedNodes.back();  // next node to be computed "k"
		//AffectsTreeVisitedNodes.back().isVisited = true;
		//AffectsTreeVisitedNodes.pop_back();

		//VisitedNodes.push_back( UnVisitedNodes.back() ); // 
		resVector.push_back( UnVisitedNodes.back() ); // 
		currStmt = UnVisitedNodes.back();
		UnVisitedNodes.pop_back();

		v1.clear();
		m.getAffectedby(v1, v2, arg1.argument, arg2.argument,arg2.argumentType); // get all statements that affect

		for( int i=0; i<v1.size(); i++ ) { // store all statements that affect "k"
			//if( currStmt == s2 ) // found s2; therefore Affects*(s1, s2) is true
			//	return true;
			//currStmt = v2.at(i);
			//a.stmtID = currStmt; a.isVisited = false;
			//if( !containsAffects( AffectsTreeVisitedNodes, it, currStmt ) )
			//AffectsTreeVisitedNodes.push_back( a );

			//if ( !searchStmtList(v2.at(i), VisitedNodes, iit ) && !searchStmtList(v2.at(i), UnVisitedNodes, iit ) )
			if ( !searchStmtList(v1.at(i), resVector, iit ) && !searchStmtList(v1.at(i), UnVisitedNodes, iit ) )
				UnVisitedNodes.push_back(v1.at(i));
		};
	}
	return false;
};

int main() {
	clock_t startclk, endclk;
	double diff;

	AST *ast = AST::getInstance(); 
	AST_Builder *ab = AST_Builder::getInstance(); 
	AST_Query aq (ast); aq.resetAST_Query();

	//AST_testBuild(ab);
	//STRING srcFile = "C:\\Users\\forty7\\Dropbox\\TestCases\\test\\s.txt";
	STRING srcFile = "D:\\Dropbox\\TestCases\\Affects-AffectsStar\\s-49.txt";

	startclk = clock();

	Parser2 p = Parser2();
	STATUS _status = p.parse(srcFile);

	DesignExtractor DesExt;
	DesExt.fillTable();
	StatTable *st = StatTable::getInstance();
	ProcTable *pt = ProcTable::getInstance();
	QueryProcessor qp;

	//Argument arg1, arg2;
	//PAT_Argument patarg;


	// <-- Affects cache tests
	/*cout << "affects cache tests";
	AuxAffects *aa = AuxAffects::getInstance();
	int numAssign = 500;
	aa->initialize(numAssign);
	aa->setAffects(167, 5);
	aa->setAffects(1, 4);
	aa->setAffects(4, 3);
		for (unsigned int s1=1; s1<=numAssign; s1++) {
			for (unsigned int s2=1; s2<=numAssign; s2++) {
				if( aa->isAuxAffectsAvailable(s1, s2 ) == true )
					cout << "< " << s1 << ", " << s2 << " >  \n";
			}
		}*/
	// -->

	// Affects tests
	/*vector<STATEMENT_INDEX> v1, v2;
	cout << "affects B-) ";
	Argument arg1,arg2;
	arg1.argumentType = INTARGUMENT;
	arg1.argument = 1;
	arg2.argumentType = ARGUMENT;
	arg2.argument = _STMT; 
	qp.getAffectsT(v1, v2, arg1, arg2 );
	for (unsigned int i=0; i<v2.size(); i++) {
		cout << v2.at(i) << " ";
	}*/
	// end affects test


	// patern tests....
	/*
	arg1.argumentType = ARGUMENT;
	arg1.argument = _ASSIGN;
	arg2.argumentType = ARGUMENT;
	arg2.argument = _UNDERSCORE;

	patarg.stringArgument = " a +  b ";
	patarg.position = PatternPos::PATTERN_end;

	mock.getPattern(v1, arg1, arg2, patarg );
	for ( unsigned int i=0; i<v1.size(); i++ ) {
	cout << v1.at(i) << " ";
	}
	*/
	//end pattern tests..

	// affects
	/*
	arg1.argumentType = INTARGUMENT;
	arg1.argument = s1;
	arg2.argumentType = ARGUMENT;
	arg2.argument = _STMT;
	*/




	endclk = clock();
	diff = (endclk - startclk ) / (double)CLOCKS_PER_SEC;
	printf("\ntime: %.6lf", diff );
	sudhir_waitOnExit ();
	return 0;
}